---------------------------------------------------------------------------
-- Display Sound Channels data
-- by AnS, 2012
---------------------------------------------------------------------------
-- Showcases following functions:
-- * sound.get()
---------------------------------------------------------------------------
-- Usage:
-- The script allows you to observe data of 5 sound channels.
-- Analysing such info you can notice some regularities in how
-- the data oscillates/changes when ingame music plays.
-- Then you can write scripts similar to \taseditor\TrackNoise.lua
-- to facilitate synchronous dances in TAS movies.
---------------------------------------------------------------------------

square1 = {x=1, y=8, on=true};
square2 = {x=129, y=8, on=true};
triangle = {x=1, y=67, on=true};
noise = {x=129, y=67, on=true};
dpcm = {x=1, y=118, on=true};

semitones = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
duty_ratio = {"12.5%", "25%", "50%", "75%"};
duty_color = {"purple", "magenta", "orange", "yellow"};
key_color = {"#000000FF", "#202020FF", "#205050FF", "#008080FF", "#00C0C0FF", "#00FFFFFF", "#FFFFFFFF", "#FFFF00FF", "#FFFFFFFF", "#FFFF00FF", "#FFFFFFFF", "#FFFF00FF", "#FFFFFFFF", "#FFFF00FF", "#FFFFFFFF"};
noise_color = {long="orange", short = "yellow"};

sample_names = {
	-- Here you can specify custom names for DMC samples (to display name instead of just address)
	-- For example, here are two samples for Contra(U) ROM
	Sample_FC00 = {name="Kick", color="#C0C0C0FF"},
	Sample_FCC0 = {name="Snare", color="#80C0FFFF"},
}

function everyframe()
	snd = sound.get();

	if (square1.on) then
		gui.text(square1.x, square1.y, "SQUARE 1:");

		Volume = snd.rp2a03.square1.volume;
		gui.text(square1.x, square1.y + 8, "Volume:");
		gui.box(square1.x + 32, square1.y + 7, square1.x + 32+15, square1.y + 15, "#006000FF", "black");
		gui.box(square1.x + 32, square1.y + 7, square1.x + 32 + Volume * 15, square1.y + 15, "green", "clear");
		gui.text(square1.x + 48, square1.y + 8, string.format("%g", Volume));

		Frequency = snd.rp2a03.square1.frequency;
		gui.text(square1.x, square1.y + 16, "Frequency: " .. string.format("%g", Frequency) .. "Hz");

		Midikey = snd.rp2a03.square1.midikey;
		gui.box(square1.x-1, square1.y + 23, square1.x + 127, square1.y + 31, "#008080FF", "black")
		gui.box(square1.x-1, square1.y + 23, square1.x-1 + Midikey, square1.y + 31, "#00C0C0FF", "black")
		Keycolor = key_color[math.floor(Volume * 15) + 1];
		gui.box(square1.x-1 + Midikey-1, square1.y + 23, square1.x-1 + Midikey+1, square1.y + 31, "white", Keycolor)
		Note = math.floor((Midikey - 21) % 12);
		Octave = math.floor((Midikey - 12) / 12);
		Semitone = tostring(semitones[Note + 1]) .. Octave;
		gui.text(square1.x, square1.y + 32, "Note: " .. Semitone .. "  ");
		gui.text(square1.x + 50, square1.y + 32, "(" .. string.format("%g", Midikey) .. ")");

		Period = snd.rp2a03.square1.regs.frequency;
		IndicatorColor = "#" .. string.format("%x", Period % 256) .. "0000FF";
		gui.text(square1.x, square1.y + 40, "Period:");
		gui.box(square1.x + 31, square1.y + 39, square1.x + 39, square1.y + 47, IndicatorColor, "black");
		gui.text(square1.x + 40, square1.y + 40, string.format("%g", Period));

		Duty = snd.rp2a03.square1.duty;
		gui.text(square1.x, square1.y + 48, "Duty #" .. Duty .. " (" .. duty_ratio[Duty + 1] .. ")", duty_color[Duty + 1]);
	end

	if (square2.on) then
		gui.text(square2.x, square2.y, "SQUARE 2:");

		Volume = snd.rp2a03.square2.volume;
		gui.text(square2.x, square2.y + 8, "Volume:");
		gui.box(square2.x + 32, square2.y + 7, square2.x + 32+15, square2.y + 15, "#006000FF", "black");
		gui.box(square2.x + 32, square2.y + 7, square2.x + 32 + Volume * 15, square2.y + 15, "green", "clear");
		gui.text(square2.x + 48, square2.y + 8, string.format("%g", Volume));

		Frequency = snd.rp2a03.square2.frequency;
		gui.text(square2.x, square2.y + 16, "Frequency: " .. string.format("%g", Frequency) .. "Hz");

		Midikey = snd.rp2a03.square2.midikey;
		gui.box(square2.x-1, square2.y + 23, square2.x + 127, square2.y + 31, "#008080FF", "black")
		gui.box(square2.x-1, square2.y + 23, square2.x-1 + Midikey, square2.y + 31, "#00C0C0FF", "black")
		Keycolor = key_color[math.floor(Volume * 15) + 1];
		gui.box(square2.x-1 + Midikey-1, square2.y + 23, square2.x-1 + Midikey+1, square2.y + 31, "white", Keycolor)
		Note = math.floor((Midikey - 21) % 12);
		Octave = math.floor((Midikey - 12) / 12);
		Semitone = tostring(semitones[Note + 1]) .. Octave;
		gui.text(square2.x, square2.y + 32, "Note: " .. Semitone .. "  ");
		gui.text(square2.x + 50, square2.y + 32, "(" .. string.format("%g", Midikey) .. ")");

		Period = snd.rp2a03.square2.regs.frequency;
		IndicatorColor = "#" .. string.format("%x", Period % 256) .. "0000FF";
		gui.text(square2.x, square2.y + 40, "Period:");
		gui.box(square2.x + 31, square2.y + 39, square2.x + 39, square2.y + 47, IndicatorColor, "black");
		gui.text(square2.x + 40, square2.y + 40, string.format("%g", Period));

		Duty = snd.rp2a03.square2.duty;
		gui.text(square2.x, square2.y + 48, "Duty #" .. Duty .. " (" .. duty_ratio[Duty + 1] .. ")", duty_color[Duty + 1]);
	end

	if (triangle.on) then
		gui.text(triangle.x, triangle.y, "TRIANGLE:");

		Volume = snd.rp2a03.triangle.volume;
		gui.text(triangle.x, triangle.y + 8, "Volume:");
		gui.box(triangle.x + 32, triangle.y + 7, triangle.x + 32+15, triangle.y + 15, "#006000FF", "black");
		gui.box(triangle.x + 32, triangle.y + 7, triangle.x + 32 + Volume * 15, triangle.y + 15, "#00A000FF", "clear");
		gui.text(triangle.x + 48, triangle.y + 8, string.format("%g", Volume));

		Frequency = snd.rp2a03.triangle.frequency;
		gui.text(triangle.x, triangle.y + 16, "Frequency: " .. string.format("%g", Frequency) .. "Hz");

		Midikey = snd.rp2a03.triangle.midikey;
		gui.box(triangle.x-1, triangle.y + 23, triangle.x + 127, triangle.y + 31, "#008080FF", "black")
		gui.box(triangle.x-1, triangle.y + 23, triangle.x-1 + Midikey, triangle.y + 31, "#00C0C0FF", "black")
		if (Volume ~= 0) then gui.box(triangle.x-1 + Midikey-1, triangle.y + 23, triangle.x-1 + Midikey+1, triangle.y + 31, "white", "black") end;
		Note = math.floor((Midikey - 21) % 12);
		Octave = math.floor((Midikey - 12) / 12);
		Semitone = tostring(semitones[Note + 1]) .. Octave;
		gui.text(triangle.x, triangle.y + 32, "Note: " .. Semitone .. "  ");
		gui.text(triangle.x + 50, triangle.y + 32, "(" .. string.format("%g", Midikey) .. ")");

		Period = snd.rp2a03.triangle.regs.frequency;
		IndicatorColor = "#" .. string.format("%x", Period % 256) .. "0000FF";
		gui.text(triangle.x, triangle.y + 40, "Period:");
		gui.box(triangle.x + 31, triangle.y + 39, triangle.x + 39, triangle.y + 47, IndicatorColor, "black");
		gui.text(triangle.x + 40, triangle.y + 40, string.format("%g", Period));
	end

	if (noise.on) then
		gui.text(noise.x, noise.y, "NOISE:");

		Volume = snd.rp2a03.noise.volume;
		gui.text(noise.x, noise.y + 8, "Volume:");
		gui.box(noise.x + 32, noise.y + 7, noise.x + 32+15, noise.y + 15, "#006000FF", "black");
		gui.box(noise.x + 32, noise.y + 7, noise.x + 32 + Volume * 15, noise.y + 15, "green", "clear");
		gui.text(noise.x + 48, noise.y + 8, string.format("%g", Volume));

		Frequency = snd.rp2a03.noise.frequency;
		gui.text(noise.x, noise.y + 16, "Frequency: " .. string.format("%g", Frequency) .. "Hz");

		Midikey = snd.rp2a03.noise.midikey;
		gui.box(noise.x-1, noise.y + 23, noise.x + 127, noise.y + 31, "#008080FF", "black")
		gui.box(noise.x-1, noise.y + 23, noise.x-1 + (Midikey/2), noise.y + 31, "#00C0C0FF", "black")
		Keycolor = key_color[math.floor(Volume * 15) + 1];
		gui.box(noise.x-1 + (Midikey/2)-1, noise.y + 23, noise.x-1 + (Midikey/2)+1, noise.y + 31, "white", Keycolor)
		Note = math.floor((Midikey - 21) % 12);
		Octave = math.floor((Midikey - 12) / 12);
		Semitone = tostring(semitones[Note + 1]) .. Octave;
		gui.text(noise.x, noise.y + 32, "Note: " .. Semitone .. "   ");
		gui.text(noise.x + 58, noise.y + 32, "(" .. string.format("%g", Midikey) .. ")");

		Period = snd.rp2a03.noise.regs.frequency;
		IndicatorColor = "#" .. string.format("%X", Period % 256) .. "0000FF";
		gui.text(noise.x, noise.y + 40, "Period:");
		gui.box(noise.x + 31, noise.y + 39, noise.x + 39, noise.y + 47, IndicatorColor, "black");
		gui.text(noise.x + 40, noise.y + 40, string.format("%g", Period));

		if (snd.rp2a03.noise.short) then
			gui.text(noise.x, noise.y + 48, "Mode: short", noise_color.short);
		else
			gui.text(noise.x, noise.y + 48, "Mode: long", noise_color.long);
		end
	end

	if (dpcm.on) then
		gui.text(dpcm.x, dpcm.y, "DPCM:");

		Volume = snd.rp2a03.dpcm.volume;
		gui.text(dpcm.x, dpcm.y + 8, "Volume:");
		gui.box(dpcm.x + 32, dpcm.y + 7, dpcm.x + 32+15, dpcm.y + 15, "#006000FF", "black");
		gui.box(dpcm.x + 32, dpcm.y + 7, dpcm.x + 32 + Volume * 15, dpcm.y + 15, "#00A000FF", "clear");
		gui.text(dpcm.x + 48, dpcm.y + 8, string.format("%g", Volume));

		Frequency = snd.rp2a03.dpcm.frequency;
		gui.text(dpcm.x, dpcm.y + 16, "Frequency: " .. string.format("%g", Frequency) .. "Hz");

		Midikey = snd.rp2a03.dpcm.midikey;
		gui.box(dpcm.x-1, dpcm.y + 23, dpcm.x + 127, dpcm.y + 31, "#008080FF", "black")
		gui.box(dpcm.x-1, dpcm.y + 23, dpcm.x-1 + (Midikey/2), dpcm.y + 31, "#00C0C0FF", "black")
		if (Volume ~= 0) then gui.box(dpcm.x-1 + (Midikey/2)-1, dpcm.y + 23, dpcm.x-1 + (Midikey/2)+1, dpcm.y + 31, "white", "black") end;
		Note = math.floor((Midikey - 21) % 12);
		Octave = math.floor((Midikey - 12) / 12);
		Semitone = tostring(semitones[Note + 1]) .. Octave;
		gui.text(dpcm.x, dpcm.y + 32, "Note: " .. Semitone .. "  ");
		gui.text(dpcm.x + 50, dpcm.y + 32, "(" .. string.format("%g", Midikey) .. ")");

		Period = snd.rp2a03.dpcm.regs.frequency;
		IndicatorColor = "#" .. string.format("%x", Period % 256) .. "0000FF";
		gui.text(dpcm.x, dpcm.y + 40, "Period:");
		gui.box(dpcm.x + 31, dpcm.y + 39, dpcm.x + 39, dpcm.y + 47, IndicatorColor, "black");
		gui.text(dpcm.x + 40, dpcm.y + 40, string.format("%g", Period));

		if (snd.rp2a03.dpcm.dmcloop) then
			gui.text(dpcm.x, dpcm.y + 48, "Sample: $" .. string.format("%X", snd.rp2a03.dpcm.dmcaddress) .. " (" .. snd.rp2a03.dpcm.dmcsize .. " bytes looped)", "orange");
		else
			gui.text(dpcm.x, dpcm.y + 48, "Sample: $" .. string.format("%X", snd.rp2a03.dpcm.dmcaddress) .. " (" .. snd.rp2a03.dpcm.dmcsize .. " bytes)");
		end

		Sample_id = "Sample_" .. string.format("%X", snd.rp2a03.dpcm.dmcaddress);
		if (sample_names[Sample_id] ~= nil) then
			gui.text(dpcm.x + 170, dpcm.y + 48, sample_names[Sample_id].name, sample_names[Sample_id].color);
		end

		gui.text(dpcm.x, dpcm.y + 56, "DMC seed: " .. string.format("%X", snd.rp2a03.dpcm.dmcseed));
	end
end

emu.registerafter(everyframe);

